﻿using Clancy.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Clancy.Common
{
    public static class PointExtensions
    {
        public static void SetLocationToGrabPoint(this Ellipse grabPoint, Point position, int radius)
        {
            Canvas.SetLeft(grabPoint, position.X - radius);
            Canvas.SetTop(grabPoint, position.Y - radius);
        }

        public static IEnumerable<MotionZonePoint> SortClockwise(this IEnumerable<MotionZonePoint> points)
        {
            List<MotionZonePoint> workingPoints = new List<MotionZonePoint>(points);
            if (!workingPoints.Any())
                return workingPoints;

            MotionZonePoint center = GetCenter(workingPoints);

            PointAngleComparer pointAngleComparer = new PointAngleComparer(center, false);
            workingPoints.Sort(pointAngleComparer);
            return workingPoints;
        }

        public static System.Drawing.Point ToDrawingPoint(this Point point)
        {
            return new System.Drawing.Point((int)point.X, (int)point.Y);
        }

        public static Ellipse ToGrabPoint(this System.Drawing.Point point, int radius, Brush strokeBrush, double strokeThickness)
        {
            Ellipse ellipse = new Ellipse { Width = (2 * radius) + 1, Height = (2 * radius) + 1, Stroke = strokeBrush, StrokeThickness = strokeThickness };
            Canvas.SetLeft(ellipse, point.X - radius);
            Canvas.SetTop(ellipse, point.Y - radius);
            return ellipse;
        }

        public static Ellipse ToGrabPoint(this Point point, int radius, Brush strokeBrush, double strokeThickness)
        {
            Ellipse ellipse = new Ellipse { Width = (2 * radius) + 1, Height = (2 * radius) + 1, Stroke = strokeBrush, StrokeThickness = strokeThickness };
            Canvas.SetLeft(ellipse, point.X - radius);
            Canvas.SetTop(ellipse, point.Y - radius);
            return ellipse;
        }

        public static Ellipse ToGrabPoint(this MotionZonePoint point, int radius, Brush strokeBrush, double strokeThickness, Brush fillBrush)
        {
            Ellipse ellipse = new Ellipse
            {
                Width = (2 * radius) + 1,
                Height = (2 * radius) + 1,
                Stroke = strokeBrush,
                StrokeThickness = strokeThickness,
                Fill = fillBrush
            };
            Canvas.SetLeft(ellipse, point.X - radius);
            Canvas.SetTop(ellipse, point.Y - radius);
            return ellipse;
        }

        public static MotionZonePoint ToMotionZonePoint(this Point point)
        {
            return new MotionZonePoint((int)point.X, (int)point.Y);
        }

        public static Point ToSystemWindowsPoint(this System.Drawing.Point point)
        {
            return new Point(point.X, point.Y);
        }

        public static Point ToSystemWindowsPoint(this MotionZonePoint point)
        {
            return new Point(point.X, point.Y);
        }

        public static void UpdatePosition(this MotionZonePoint point, Point position)
        {
            point.X = (int)position.X;
            point.Y = (int)position.Y;
        }

        public static void UpdatePosition(this Ellipse grabPoint, MotionZonePoint point)
        {
            Canvas.SetLeft(grabPoint, point.X);
            Canvas.SetTop(grabPoint, point.Y);
        }

        public static void UpdatePosition(this MotionZonePoint point, double left, double top)
        {
            point.X = (int)left;
            point.Y = (int)top;
        }

        private static MotionZonePoint GetCenter(IEnumerable<MotionZonePoint> points)
        {
            int xCenter = 0;
            int yCenter = 0;

            IEnumerable<MotionZonePoint> workPoints = points as MotionZonePoint[] ?? points.ToArray();
            foreach (var point in workPoints)
            {
                xCenter = xCenter + point.X;
                yCenter = yCenter + point.Y;
            }

            MotionZonePoint center = new MotionZonePoint(xCenter / workPoints.Count(), yCenter / workPoints.Count());
            return center;
        }

        private class PointAngleComparer : IComparer<MotionZonePoint>
        {
            private readonly MotionZonePoint m_CenterPoint;
            private readonly bool m_CounterClockWise;

            public PointAngleComparer(MotionZonePoint centerPoint, bool counterClockWise)
            {
                m_CenterPoint = centerPoint;
                m_CounterClockWise = counterClockWise;
            }

            public int Compare(MotionZonePoint point1, MotionZonePoint point2)
            {
                double anglePoint1 = GetAngleDegree(m_CenterPoint, point1);
                double anglePoint2 = GetAngleDegree(m_CenterPoint, point2);

                if (m_CounterClockWise)
                {
                    return (int)((anglePoint1 - anglePoint2) * 1000);
                }
                return (int)((anglePoint2 - anglePoint1) * 1000);
            }

            private static double GetAngleDegree(MotionZonePoint origin, MotionZonePoint target)
            {
                double n = 270 - (Math.Atan2(origin.Y - target.Y, origin.X - target.X)) * 180 / Math.PI;
                return n % 360;
            }
        }
    }
}